﻿using System;
using System.Collections.Generic;
using System.IO;

namespace ConsoleApplication1
{
    public class Algorithm
    {
        // Bieżąca populacja algorytmu
        public List<bool[]> curr_population;
        public bool[] best_expert;
        // Tablica wartości f. celu
        public double[] zarobki;
        // Rozmiar populacji
        int N;
        // Rozmiar genotypu = liczba reguł
        int D;
        // Liczba iteracji 
        int iter;
        // pr. mutacji
        double teta;
        // pr. użycia operatorów krzyżowania
        double uniformPr, halfPr, onePr, twoPr;
        // pr. użycia strategii replacementu
        double fullPr, bestPopChilPr, bestOfPairPr, replaceKPr;

        public Algorithm(int N, int iter, double teta, double uniformPr, double halfPr, double onePr, double twoPr, double fullPr, double bestPopChilPr, double bestOfPairPr, double replaceKPr)
        {
            this.N = N;
            this.D = getAllIndex().Length;
            this.iter = iter;
            this.teta = teta;

            this.uniformPr = uniformPr;
            this.halfPr = halfPr;
            this.onePr = onePr;
            this.twoPr = twoPr;

            this.fullPr = fullPr;
            this.bestPopChilPr = bestPopChilPr;
            this.bestOfPairPr = bestOfPairPr;
            this.replaceKPr = replaceKPr;

            initPopulation();
        }

        // Funkcja inicjująca losową populację 
        public void initPopulation()
        {
            curr_population = new List<bool[]>();
            bool[] curr_genotype = new bool[D];
            Random generator = new Random();

            for (int i = 0; i < N; i++)
            {
                curr_genotype = new bool[D];
                for (int j = 0; j < D; j++)
                {
                    if (generator.Next(2) == 0)
                    {
                        curr_genotype[j] = false;
                    }
                    else
                    {
                        curr_genotype[j] = true;
                    }
                }
               
                curr_population.Add(curr_genotype);
            }

            //save_pop();
        }

        public void save_pop()
        {
            TextWriter tw = new StreamWriter("date.txt");

            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < D; j++)
                {
                    if (curr_population[i][j])
                        tw.Write("1");
                    else
                        tw.Write("0");
                }
                tw.WriteLine();
            }

            // close the stream
            tw.Close();
        }

        // Ocena populacji -- f. sterująca, zwaraca odpowiadającą populacji listę wartości f. przystosowania 
        public double[] evalPopulation(List<bool[]> population, DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            Triple<double, double, double[]> t = objective_sum_min(population, dateFrom, dateTo, path, cash);
            double min = t.First;
            double sum = t.Second;
            double[] objective_vector = t.Third;

            return adaptation(min, sum, objective_vector);
        }

        // Funkcja przystosowania, zwraca wektor przystosowania
        public double[] adaptation(double min, double sum, double[] objective_vector)
        {
            double[] adaptation_vector = new double[objective_vector.Length];

            zarobki = objective_vector;

            for (int i = 0; i < objective_vector.Length; i++)
            {
                adaptation_vector[i] = (double)((objective_vector[i] - min) / (sum - objective_vector.Length * min));
            }

            return adaptation_vector;
        }

        public Triple<double, double, double[]> objective_sum_min(List<bool[]> population, DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            // suma wszystkich wartości f. celu
            double sum = 0;
            // min (najgorsza wart. f. celu)
            double min = double.MaxValue;
            // tablica kolejnych wart. f. celu dla kolejnych osbników
            double[] objective = new double[population.Count];

            Index[] indexes = getAllIndex();
            Index m;
            List<Index.Recomendation> recomendations;

            List<Record> list = Data.readFromFile(path, dateFrom, dateTo);
            int days = list.Count;

            // tablica będzie trzymać decyzję i-tej reguły, danego dnia (-1,0,1)
            Index.Recomendation[,] table = new Index.Recomendation[D, days];

            // uzupełnianie powyzszej tablicy
            for (int j = 0; j < D; j++)
            {
                m = indexes[j];

                for (int k = 0; k < days; k++)
                {
                    Index.Recomendation rec = m.eval(list, k + 1);

                    table[j, k] = rec;                    
                }
            }

            // sprawdzanie co robią kolejni osobnicy
            for (int i = 0; i < population.Count; i++)
            {
                 recomendations = new List<Index.Recomendation>();

                 // dla kolejnych dni
                 for (int k = 0; k < days; k++)
                 {
                     int decisionNone = 0;
                     int decisionBuy = 0;
                     int decisionSell = 0;                     

                     for (int j = 0; j < D; j++)
                     {
                         if (population[i][j])
                         {
                             if (table[j, k] == Index.Recomendation.buy)
                                 decisionBuy++;
                             else if (table[j, k] == Index.Recomendation.sell)
                                 decisionSell++;
                             else
                                 decisionNone++;
                         }
                     }
                     int x = decisionBuy + decisionNone + decisionSell;
                     if (decisionSell > decisionNone && decisionSell > decisionBuy)
                         recomendations.Add(Index.Recomendation.sell);
                     else if (decisionBuy > decisionNone && decisionBuy > decisionSell)
                         recomendations.Add(Index.Recomendation.buy);
                     else
                         recomendations.Add(Index.Recomendation.none);
                 }

                 // zarobek i-tego osobnika
                 objective[i] = (double)countEarnings(list, recomendations, (int)(cash / list[0].Close), cash);

                 if (objective[i] < min)
                     min = objective[i];
                 int xy = (int)(cash / list[0].Close);

                 sum += objective[i];
            }

            return new Triple<double, double, double[]>(min, sum, objective);
        }

        public double sumuj(double[] adaptation_vector)
        {
            double sum = 0.0f;

            for (int i = 0; i < N; i++)
            {
                sum += adaptation_vector[i];
            }

            return sum;
        }



        // selekcja ruletkowa
        public List<bool[]> roulette_seletion(double[] adaptation_vector)
        {

            List<bool[]> parents = new List<bool[]>();
            Random generator = new Random();
            double curr_num;
            int k;

            for (int i = 0; i < N; i++)
            {
                curr_num = (double) generator.NextDouble();
                //Console.WriteLine(curr_num.ToString());
                k = -1;
                double j = 0.0f;

                while (j < curr_num)
                {
                    k++;
                    if (k < N)
                        j += adaptation_vector[k];
                    else
                    {
                        k--;
                        break;
                    }
                }

                parents.Add(curr_population[k]) ;
            }

            return parents;
        }

        public void show_pop()
        {
            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < D; j++)
                {
                    if (curr_population[i][j])
                        Console.Write(" 1");
                    else
                        Console.Write(" 0");
                }
                Console.WriteLine();
            }
        }


        public void show_zarobki()
        {
            for (int i = 0; i < N; i++)
            {
                Console.WriteLine(zarobki[i].ToString());
            }
        }

        // Zwraca maksymalny zarobek (szablon całego algorytmu)
        public Pair<double, bool[]> doTheEvolution(DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            double[] adaptation = evalPopulation(curr_population, dateFrom, dateTo, path, cash);
            double[] children_adaptation, parents_adaptation;
            double op_prob, rep_prob;
            List<bool[]> parents;
            List<bool[]> children;
            CrossoverOperator uc;
            Mutation m;
            Replacement r;
            double max = double.MinValue;
            double inner_max = double.MinValue;
            //TextWriter tw = new StreamWriter("pekao.txt");

            Random generator = new Random();

            for (int i = 0; i < iter; i++)
            {
                Console.WriteLine(i.ToString());
                // wyświetla zarobki poszczególnych ekspertów
                //show_pop();
                //show_zarobki();
                //Console.WriteLine();
                parents = roulette_seletion(adaptation);
                //Console.WriteLine(sumuj(adaptation).ToString());
                //show_pop();

                children = new List<bool[]>();

                op_prob = (double) generator.NextDouble();

                if (op_prob <= uniformPr)
                    uc = new UniformCrossover();
                else if (op_prob <= (uniformPr + halfPr))
                    uc = new HalfUniformCrossover();
                else if (op_prob <= (uniformPr + halfPr + onePr))
                    uc = new OnePointCrossover();
                else
                    uc = new TwoPointCrossover();

                if (N % 2 == 0)
                {
                    for (int j = 0; j < N; j += 2)
                    {
                        uc.parent1 = new bool[D];
                        uc.parent2 = new bool[D];

                        uc.parent1 = parents[j];
                        uc.parent2 = parents[j + 1];

                        Pair<bool[], bool[]> p = uc.doCrossover();
                        children.Add(p.First);
                        children.Add(p.Second);
                    }
                }
                else
                {
                    children.Add(parents[0]);
                    for (int j = 1; j < N; j += 2)
                    {
                        uc.parent1 = new bool[D];
                        uc.parent2 = new bool[D];

                        uc.parent1 = parents[j];
                        uc.parent2 = parents[j + 1];

                        Pair<bool[], bool[]> p = uc.doCrossover();
                        children.Add(p.First);
                        children.Add(p.Second);
                    }
                }

                m = new Mutation(teta);

                for (int j = 0; j < N; j++)
                {
                    children[j] = m.doMutation(children[j]);
                }

                rep_prob = (double)generator.NextDouble();

                if (rep_prob <= fullPr)
                     r = new FullSwapReplacement(curr_population, children);
                else if (rep_prob <= (fullPr + bestPopChilPr))
                {
                    children_adaptation = evalPopulation(children, dateFrom, dateTo, path, cash);
                    r = new BestOfPopulationChildrenReplacement(curr_population, children, adaptation, children_adaptation);
                }
                else if (rep_prob <= (fullPr + bestPopChilPr + bestOfPairPr))
                {
                    children_adaptation = evalPopulation(children, dateFrom, dateTo, path, cash);
                    parents_adaptation = evalPopulation(parents, dateFrom, dateTo, path, cash);
                    r = new BestOfPairReplacement(parents, children, parents_adaptation, children_adaptation);
                }
                else
                {
                    children_adaptation = evalPopulation(children, dateFrom, dateTo, path, cash);
                    r = new ReplaceKWorstReplacement(curr_population, children, adaptation, children_adaptation, (int) N/4);
                }


                curr_population = r.returnNewGeneration();
                adaptation = evalPopulation(curr_population, dateFrom, dateTo, path, cash);

                inner_max = double.MinValue;
                for (int x = 0; x < N; x++)
                {
                    if (max < zarobki[x])
                    {
                        max = zarobki[x];
                        best_expert = new bool[D];
                        best_expert = curr_population[x];
                    }

                    if (inner_max < zarobki[x])
                    {
                        inner_max = zarobki[x];
                    }
                    
                }
                Console.WriteLine(inner_max.ToString());

                //tw.WriteLine(i.ToString() + " " + inner_max.ToString());
                
            }

            //tw.Close();
            return new Pair<double, bool[]>(max, best_expert);
        }

        public double simulate_best_expert(DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            Index[] indexes = getAllIndex();
            Index m;
            List<Index.Recomendation> recomendations;
            
            List<Record> list = Data.readFromFile(path, dateFrom, dateTo);
            int days = list.Count;

            // tablica będzie trzymać decyzję i-tej reguły, danego dnia (-1,0,1)
            Index.Recomendation[,] table = new Index.Recomendation[D, days];

            // uzupełnianie powyzszej tablicy
            for (int j = 0; j < D; j++)
            {
                m = indexes[j];

                for (int k = 0; k < days; k++)
                {
                    Index.Recomendation rec = m.eval(list, k + 1);

                    table[j, k] = rec;
                }
            }

            recomendations = new List<Index.Recomendation>();

            // dla kolejnych dni
            for (int k = 0; k < days; k++)
            {
                int decisionNone = 0;
                int decisionBuy = 0;
                int decisionSell = 0;

                for (int j = 0; j < D; j++)
                {
                    if (best_expert[j])
                    {
                        if (table[j, k] == Index.Recomendation.buy)
                            decisionBuy++;
                        else if (table[j, k] == Index.Recomendation.sell)
                            decisionSell++;
                        else
                            decisionNone++;
                    }
                }

                if (decisionSell > decisionNone && decisionSell > decisionBuy)
                    recomendations.Add(Index.Recomendation.sell);
                else if (decisionBuy > decisionNone && decisionBuy > decisionSell)
                    recomendations.Add(Index.Recomendation.buy);
                else
                    recomendations.Add(Index.Recomendation.none);
            }

            // zarobek maksymalnego eksperta
            return (double)countEarnings(list, recomendations, (int)(cash / list[0].Close), cash);

        }

        /*
         * Liczy zysk jaki osiągneliśmy
         * Uwaga - pierwszy dzien jest pomijany w obliczeniach. Jest to baza do obliczenia poczatkowej wartosci portfela.
         * 
         * data - ceny akcji (musi miec przynajmniej jedna pozycje).
         * recomendations - rekomendacje (dlugosc dat i recomendation musi byc taka sama)
         * stocks - poczatkowa liczba akcji
         * money - poczatkowa kwota 
         * 
         * Zwraca ostateczna kwote jaka udalo sie zarobic (wartosc koncowa akcji i gotowki - wartosc poczatkowa akcji i gotowki)
         */

        public decimal countEarnings(List<Record> data, List<Index.Recomendation> recomendations, int stocks, decimal money)
        {
            //Console.WriteLine(data.Count.ToString());
            if (data.Count != recomendations.Count)
                throw new Exception("Tablice danych i rekomentacji musz miec ten sam rozmiar");

            int count = data.Count;

            decimal valueStart = money + stocks * data[0].Close;

            for (int i = 1; i < count; i++)
            {
                if (recomendations[i] == Index.Recomendation.buy)
                {
                    int toBuy = stocks;                    
                    decimal cost = toBuy * data[i].Close;
                    if (cost > money)
                    {
                        toBuy = (int)(money / data[i].Close);
                        cost = toBuy * data[i].Close;
                    }

                    stocks += toBuy;
                    money -= cost;
                }
                else if (recomendations[i] == Index.Recomendation.sell)
                {
                    int toSell = stocks / 2;
                    money += toSell * data[i].Close;
                    stocks -= toSell;
                }
            }

            decimal valueEnd = money + stocks * data[count - 1].Close;

            return valueEnd - valueStart;
        }

        public Index[] getAllIndex()
        {
            return new Index[] {
            new MovingAverage(5),
            new MovingAverage(6),
            new MovingAverage(7),
            new MovingAverage(8),
            new MovingAverage(9),
            new MovingAverage(10),
            new MovingAverage(11),
            new MovingAverage(12),
            new MovingAverage(13),
            new MovingAverage(14),
            new MovingAverage(15),
            new MovingAverage(16),
            new MovingAverage(17),
            new MovingAverage(18),
            new MovingAverage(19),
            new MovingAverage(20),
            new MovingAverage(21),
            new MovingAverage(22),
            new MovingAverage(23),
            new MovingAverage(24),
            new MovingAverage(25),
            new MovingAverage(26),
            new MovingAverage(27),
            new MovingAverage(28),
            new MovingAverage(29),
            new MovingAverage(30),
            new MovingAverage(31),
            new MovingAverage(32),

            new Macd (20, 12, 9),
            new Macd (26, 12, 9),            
            new Macd (30, 12, 9),
            new Macd (40, 12, 9),
            new Macd (20, 8, 9),
            new Macd (26, 8, 9),
            new Macd (30, 8, 9),
            new Macd (40, 8, 9),
            new Macd (20, 15, 9),
            new Macd (26, 15, 9),
            new Macd (30, 15, 9),
            new Macd (40, 15, 9),
            new Macd (20, 12, 5),
            new Macd (26, 12, 5),
            new Macd (30, 12, 5),
            new Macd (40, 12, 5),
            new Macd (20, 15, 13),
            new Macd (26, 15, 13),
            new Macd (30, 15, 13),
            new Macd (40, 15, 13),
            new Macd (50, 25, 23),
            new Macd (45, 40, 33),
            new Macd (12, 10, 9),
            new Macd (6, 4, 2),
            new Macd (8, 7, 6),
            new Macd (14, 12, 9),
            new Macd (7, 4, 2),
            new Macd (9, 7, 6),
                                   

            new Rsi(5),
            new Rsi(6),
            new Rsi(7),
            new Rsi(8),
            new Rsi(9),
            new Rsi(10),
            new Rsi(11),
            new Rsi(12),
            new Rsi(13),
            new Rsi(14),
            new Rsi(15),
            new Rsi(16),
            new Rsi(17),
            new Rsi(18),
            new Rsi(19),
            new Rsi(20),
            new Rsi(21),
            new Rsi(22),
            new Rsi(23),
            new Rsi(24),
            new Rsi(25),
            new Rsi(26),
            new Rsi(27),
            new Rsi(28),
            new Rsi(29),
            new Rsi(30),
            new Rsi(31),
            new Rsi(32),

            new Ema(5),
            new Ema(6),
            new Ema(7),
            new Ema(8),
            new Ema(9),
            new Ema(10),
            new Ema(11),
            new Ema(12),
            new Ema(13),
            new Ema(14),
            new Ema(15),
            new Ema(16),
            new Ema(17),
            new Ema(18),
            new Ema(19),
            new Ema(20),
            new Ema(21),
            new Ema(22),
            new Ema(23),
            new Ema(24),
            new Ema(25),
            new Ema(26),
            new Ema(27),
            new Ema(28),
            new Ema(29),
            new Ema(30),
            new Ema(31),
            new Ema(32),

            new Wma(5),
            new Wma(6),
            new Wma(7),
            new Wma(8),
            new Wma(9),
            new Wma(10),
            new Wma(11),
            new Wma(12),
            new Wma(13),
            new Wma(14),
            new Wma(15),
            new Wma(16),
            new Wma(17),
            new Wma(18),
            new Wma(19),
            new Wma(20),
            new Wma(21),
            new Wma(22),
            new Wma(23),
            new Wma(24),
            new Wma(25),
            new Wma(26),
            new Wma(27),
            new Wma(28),
            new Wma(29),
            new Wma(30),
            new Wma(31),
            new Wma(32),
            

            new EmaMa(5, 20),
            new EmaMa(6, 19),
            new EmaMa(7, 18),
            new EmaMa(8, 17),
            new EmaMa(9, 16),
            new EmaMa(10, 15),
            new EmaMa(11, 14),
            new EmaMa(12, 13),
            new EmaMa(13, 12),
            new EmaMa(14, 11),
            new EmaMa(15, 10),
            new EmaMa(16, 9),
            new EmaMa(17, 8),
            new EmaMa(18, 7),
            new EmaMa(19, 6),
            new EmaMa(20, 5),
            new EmaMa(10, 10),
            new EmaMa(25, 15),
            new EmaMa(35, 25),
            new EmaMa(25, 35),
            new EmaMa(9, 9),
            new EmaMa(5, 5),
            new EmaMa(11, 5),
            new EmaMa(22, 18),
            new EmaMa(18, 14),
            new EmaMa(31, 21),
            new EmaMa(41, 21),

            new WmaMa(5, 20),
            new WmaMa(6, 19),
            new WmaMa(7, 18),
            new WmaMa(8, 17),
            new WmaMa(9, 16),
            new WmaMa(10, 15),
            new WmaMa(11, 14),
            new WmaMa(12, 13),
            new WmaMa(13, 12),
            new WmaMa(14, 11),
            new WmaMa(15, 10),
            new WmaMa(16, 9),
            new WmaMa(17, 8),
            new WmaMa(18, 7),
            new WmaMa(19, 6),
            new WmaMa(20, 5),
            new WmaMa(10, 10),
            new WmaMa(25, 15),
            new WmaMa(35, 25),
            new WmaMa(25, 35),
            new WmaMa(9, 9),
            new WmaMa(5, 5),
            new WmaMa(11, 5),
            new WmaMa(22, 18),
            new WmaMa(18, 14),
            new WmaMa(31, 21),
            new WmaMa(41, 21),

            new RWilliams(5),
            new RWilliams(6),
            new RWilliams(7),
            new RWilliams(8),
            new RWilliams(9),
            new RWilliams(10),
            new RWilliams(11),
            new RWilliams(12),
            new RWilliams(13),
            new RWilliams(14),
            new RWilliams(15),
            new RWilliams(16),
            new RWilliams(17),
            new RWilliams(18),
            new RWilliams(19),
            new RWilliams(20),
            new RWilliams(21),
            new RWilliams(22),
            new RWilliams(23),
            new RWilliams(24),
            new RWilliams(25),
            new RWilliams(26),
            new RWilliams(27),
            new RWilliams(28),
            new RWilliams(29),
            new RWilliams(30),
            new RWilliams(31),
            new RWilliams(32),

            new MoneyFlow(5),
            new MoneyFlow(6),
            new MoneyFlow(7),
            new MoneyFlow(8),
            new MoneyFlow(9),
            new MoneyFlow(10),
            new MoneyFlow(11),
            new MoneyFlow(12),
            new MoneyFlow(13),
            new MoneyFlow(14),
            new MoneyFlow(15),
            new MoneyFlow(16),
            new MoneyFlow(17),
            new MoneyFlow(18),
            new MoneyFlow(19),
            new MoneyFlow(20),
            new MoneyFlow(21),
            new MoneyFlow(22),
            new MoneyFlow(23),
            new MoneyFlow(24),
            new MoneyFlow(25),
            new MoneyFlow(26),
            new MoneyFlow(27),
            new MoneyFlow(28),
            new MoneyFlow(29),
            new MoneyFlow(30),
            new MoneyFlow(31),
            new MoneyFlow(32)


        };
        }
    }
}